{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "from torchvision import datasets, transforms\n",
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "import torchvision\n",
    "import numpy as np\n",
    "from torch.autograd import Variable\n",
    "import torchmetrics\n",
    "%matplotlib inline\n",
    "# https://blog.csdn.net/qq_41317652/article/details/121598292\n",
    "# https://blog.csdn.net/qq_45588019/article/details/120935828"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 预处理函数\n",
    "transform_func = transforms.Compose([transforms.ToTensor(),\n",
    "                                    transforms.Normalize(mean=[0.5],std=[0.5])])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true,
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# 下载训练集并预处理\n",
    "train_dataset = datasets.MNIST(root='../data/',\n",
    "\t\t\t\ttrain=True,\n",
    "\t\t\t\ttransform=transform_func,\n",
    "\t\t\t\tdownload=False)\n",
    "# 下载测试集并预处理\n",
    "test_dataset = datasets.MNIST(root='../data/',\n",
    "\t\t\t\ttrain=False,\n",
    "\t\t\t\ttransform=transform_func,\n",
    "\t\t\t\tdownload=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "batch_size = 64\n",
    "# 装载训练集，打乱顺序\n",
    "data_loader_train = torch.utils.data.DataLoader(dataset=train_dataset,\n",
    "                                                batch_size = batch_size,\n",
    "                                                shuffle = True,\n",
    "                                                num_workers=2)\n",
    "\n",
    "# 装载测试集，打乱顺序\n",
    "data_loader_test = torch.utils.data.DataLoader(dataset=test_dataset,\n",
    "                                               batch_size = batch_size,\n",
    "                                               shuffle = True,\n",
    "                                                num_workers=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# # 打印MNIST预处理结果\n",
    "# print(train_dataset[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# # 打印MNIST图片\n",
    "# images, labels = next(iter(data_loader_train))\n",
    "# img = torchvision.utils.make_grid(images)\n",
    "\n",
    "# img = img.numpy().transpose(1,2,0)\n",
    "# std = [0.5,0.5,0.5]\n",
    "# mean = [0.5,0.5,0.5]\n",
    "# img = img*std+mean\n",
    "# print([labels[i] for i in range(64)])\n",
    "# plt.imshow(img)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class CNN_wr(torch.nn.Module):\n",
    "    learning_rate=0.1\n",
    "    def __init__(self):\n",
    "        # 调用nn初始化\n",
    "        super(CNN_wr, self).__init__()\n",
    "        # 超参数\n",
    "        \n",
    "        input_feature = 1\n",
    "        \n",
    "        \n",
    "        feature_1 = 64\n",
    "        feature_2 = 128\n",
    "        \n",
    "        # 搭建第一层\n",
    "        self.conv1 = torch.nn.Sequential(torch.nn.Conv2d(input_feature,feature_1,kernel_size=5,stride=1,padding=0),\n",
    "                                         torch.nn.ReLU(),\n",
    "                                         torch.nn.MaxPool2d(kernel_size=2,stride=2))\n",
    "        # 第二层\n",
    "        self.conv2 = torch.nn.Sequential(torch.nn.Conv2d(feature_1,feature_2,kernel_size=3,stride=1,padding=0),\n",
    "                                         torch.nn.ReLU(),\n",
    "                                         torch.nn.MaxPool2d(kernel_size=2,stride=2))\n",
    "        # 全连接层\n",
    "        self.dense = torch.nn.Sequential(torch.nn.Linear(128*5*5,256),\n",
    "                                         torch.nn.ReLU(),\n",
    "                                        #  torch.nn.Dropout(p=0.5),\n",
    "                                         torch.nn.Linear(256, 10))\n",
    "    \n",
    "    def forward(self, x):\n",
    "        x = self.conv1(x)\n",
    "        x = self.conv2(x)\n",
    "        x = x.view(x.shape[0], -1)\n",
    "        x = self.dense(x)\n",
    "        return x\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "CNN_model_wr = CNN_wr()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CNN_wr(\n",
      "  (conv1): Sequential(\n",
      "    (0): Conv2d(1, 64, kernel_size=(5, 5), stride=(1, 1))\n",
      "    (1): ReLU()\n",
      "    (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  )\n",
      "  (conv2): Sequential(\n",
      "    (0): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1))\n",
      "    (1): ReLU()\n",
      "    (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  )\n",
      "  (dense): Sequential(\n",
      "    (0): Linear(in_features=3200, out_features=256, bias=True)\n",
      "    (1): ReLU()\n",
      "    (2): Linear(in_features=256, out_features=10, bias=True)\n",
      "  )\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "print(CNN_model_wr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 损失函数和优化\n",
    "loss_CE = torch.nn.CrossEntropyLoss()\n",
    "optimizer = torch.optim.SGD(CNN_model_wr.parameters(), lr=CNN_model_wr.learning_rate)\n",
    "epochs = 10\n",
    "\n",
    "# 读取模型参数\n",
    "if os.path.exists('../data/model_parameter.pkl'):\n",
    "    CNN_model_wr.load_state_dict(torch.load('../data/model_parameter.pkl'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 0/10\n",
      "----------\n",
      "Loss: 0.0027, Train Accuracy: 94.5683%, Test Accuracy: 97.8800%\n",
      "Torch Metrics Acc: 97.8800%\n",
      "Recall(0~9): 0.9786\n",
      "Precision(0~9): 0.9791\n",
      "f1 score: 1.0002\n",
      "Auc: 0.9983\n",
      "\n",
      "Epoch 1/10\n",
      "----------\n",
      "Loss: 0.0007, Train Accuracy: 98.5717%, Test Accuracy: 98.7600%\n",
      "Torch Metrics Acc: 98.7600%\n",
      "Recall(0~9): 0.9874\n",
      "Precision(0~9): 0.9877\n",
      "f1 score: 1.0002\n",
      "Auc: 0.9988\n",
      "\n",
      "Epoch 2/10\n",
      "----------\n",
      "Loss: 0.0005, Train Accuracy: 99.0117%, Test Accuracy: 99.1300%\n",
      "Torch Metrics Acc: 99.1300%\n",
      "Recall(0~9): 0.9913\n",
      "Precision(0~9): 0.9913\n",
      "f1 score: 1.0000\n",
      "Auc: 0.9992\n",
      "\n",
      "Epoch 3/10\n",
      "----------\n",
      "Loss: 0.0004, Train Accuracy: 99.2400%, Test Accuracy: 99.0400%\n",
      "Torch Metrics Acc: 99.0400%\n",
      "Recall(0~9): 0.9903\n",
      "Precision(0~9): 0.9903\n",
      "f1 score: 1.0000\n",
      "Auc: 0.9990\n",
      "\n",
      "Epoch 4/10\n",
      "----------\n",
      "Loss: 0.0003, Train Accuracy: 99.4450%, Test Accuracy: 98.9900%\n",
      "Torch Metrics Acc: 98.9900%\n",
      "Recall(0~9): 0.9895\n",
      "Precision(0~9): 0.9900\n",
      "f1 score: 1.0002\n",
      "Auc: 0.9988\n",
      "\n",
      "Epoch 5/10\n",
      "----------\n",
      "Loss: 0.0002, Train Accuracy: 99.5867%, Test Accuracy: 99.3000%\n",
      "Torch Metrics Acc: 99.3000%\n",
      "Recall(0~9): 0.9929\n",
      "Precision(0~9): 0.9930\n",
      "f1 score: 1.0000\n",
      "Auc: 0.9991\n",
      "\n",
      "Epoch 6/10\n",
      "----------\n",
      "Loss: 0.0002, Train Accuracy: 99.6800%, Test Accuracy: 99.2800%\n",
      "Torch Metrics Acc: 99.2800%\n",
      "Recall(0~9): 0.9927\n",
      "Precision(0~9): 0.9928\n",
      "f1 score: 1.0001\n",
      "Auc: 0.9990\n",
      "\n",
      "Epoch 7/10\n",
      "----------\n",
      "Loss: 0.0001, Train Accuracy: 99.7550%, Test Accuracy: 99.1700%\n",
      "Torch Metrics Acc: 99.1700%\n",
      "Recall(0~9): 0.9917\n",
      "Precision(0~9): 0.9917\n",
      "f1 score: 1.0000\n",
      "Auc: 0.9990\n",
      "\n",
      "Epoch 8/10\n",
      "----------\n",
      "Loss: 0.0001, Train Accuracy: 99.8183%, Test Accuracy: 99.3800%\n",
      "Torch Metrics Acc: 99.3800%\n",
      "Recall(0~9): 0.9937\n",
      "Precision(0~9): 0.9938\n",
      "f1 score: 1.0001\n",
      "Auc: 0.9992\n",
      "\n",
      "Epoch 9/10\n",
      "----------\n",
      "Loss: 0.0001, Train Accuracy: 99.8517%, Test Accuracy: 99.2700%\n",
      "Torch Metrics Acc: 99.2700%\n",
      "Recall(0~9): 0.9926\n",
      "Precision(0~9): 0.9926\n",
      "f1 score: 1.0000\n",
      "Auc: 0.9993\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")\n",
    "\n",
    "# 训练模型\n",
    "for epoch in range(epochs):\n",
    "    test_acc = torchmetrics.Accuracy()\n",
    "    test_recall = torchmetrics.Recall(average='none', num_classes=10)\n",
    "    test_precision = torchmetrics.Precision(average='none', num_classes=10)\n",
    "    test_auc = torchmetrics.AUROC(average=\"macro\", num_classes=10)\n",
    "\n",
    "    size = len(train_dataset)\n",
    "    num_batches = len(train_dataset)\n",
    "    \n",
    "    running_loss = 0.0\n",
    "    running_correct = 0\n",
    "    print(\"Epoch {}/{}\".format(epoch+1, epochs))\n",
    "    print(\"-\"*10)\n",
    "    \n",
    "    # TRAIN\n",
    "    for data in data_loader_train:\n",
    "        # __forward__\n",
    "        X_train, y_train = data\n",
    "        # 封装tensor，用于反向传播\n",
    "        X_train, y_train = Variable(X_train), Variable(y_train)\n",
    "        outputs = CNN_model_wr(X_train)\n",
    "        # 得到loss\n",
    "        loss = loss_CE(outputs, y_train)\n",
    "        # 每个batch的最大值，取其索引\n",
    "        _, index = torch.max(outputs.data, 1)\n",
    "        \n",
    "        # __backward__\n",
    "        # 清空过往梯度\n",
    "        optimizer.zero_grad()\n",
    "        # 计算梯度\n",
    "        loss.backward()\n",
    "        # 更新权重\n",
    "        optimizer.step()\n",
    "        \n",
    "        running_loss += loss.data\n",
    "        running_correct += torch.sum(index == y_train.data)\n",
    "    \n",
    "    # TEST\n",
    "    testing_correct = 0\n",
    "    for data in data_loader_test:\n",
    "        X_test, y_test = data\n",
    "        X_test, y_test = Variable(X_test), Variable(y_test)\n",
    "        outputs = CNN_model_wr(X_test)\n",
    "        _, index = torch.max(outputs.data, 1)\n",
    "        \n",
    "        testing_correct += torch.sum(index == y_test.data)\n",
    "        \n",
    "        test_acc(outputs.argmax(1), y_test)\n",
    "        test_auc.update(outputs, y_test)\n",
    "        test_recall(outputs.argmax(1), y_test)\n",
    "        test_precision(outputs.argmax(1), y_test)\n",
    "        \n",
    "    # 计算一个epoch的accuray、recall、precision、AUC\n",
    "    total_acc = test_acc.compute()\n",
    "    total_recall = test_recall.compute()\n",
    "    total_precision = test_precision.compute()\n",
    "    total_auc = test_auc.compute()\n",
    "    total_f1 = 2* (total_precision.mean(0)) / (total_precision.mean(0) + total_recall.mean(0))\n",
    "    \n",
    "    print(\"Loss: {:.4f}, Train Accuracy: {:.4f}%, Test Accuracy: {:.4f}%\".format(running_loss/len(train_dataset),\n",
    "                                                                                      100*running_correct/len(train_dataset),\n",
    "                                                                                      100*testing_correct/len(test_dataset)))\n",
    "    print(f\"Torch Metrics Acc: {(100 * total_acc):>0.4f}%\")\n",
    "    print(\"Recall(0~9): {:.4f}\".format(total_recall.mean(0)))\n",
    "    print(\"Precision(0~9): {:.4f}\".format(total_precision.mean(0)))\n",
    "    print(\"f1 score: {:.4f}\".format(total_f1))\n",
    "    print(\"Auc: {:.4f}\\n\".format(total_auc.item()))\n",
    "\n",
    "    # 清空计算对象\n",
    "    test_precision.reset()\n",
    "    test_acc.reset()\n",
    "    test_recall.reset()\n",
    "    test_auc.reset()\n",
    "    \n",
    "torch.save(CNN_model_wr.state_dict(), '../data/model_parameter.pkl')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Predict Label is: [tensor(4), tensor(7), tensor(9), tensor(0)]\n",
      "Mark Label is: [tensor(4), tensor(7), tensor(9), tensor(0)]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x26880825550>"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 实际预测\n",
    "data_loader_test = torch.utils.data.DataLoader(dataset=test_dataset,\n",
    "                                          batch_size = 4,\n",
    "                                          shuffle = True)\n",
    "X_test, y_test = next(iter(data_loader_test))\n",
    "inputs = Variable(X_test)\n",
    "pred = CNN_model_wr(inputs)\n",
    "_,pred = torch.max(pred, 1)\n",
    "\n",
    "print(\"Predict Label is:\", [ i for i in pred.data])\n",
    "print(\"Mark Label is:\",[i for i in y_test])\n",
    "\n",
    "img = torchvision.utils.make_grid(X_test)\n",
    "img = img.numpy().transpose(1,2,0)\n",
    "\n",
    "std  = [0.5,0.5,0.5]\n",
    "mean = [0.5,0.5,0.5]\n",
    "img = img*std+mean\n",
    "plt.imshow(img)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# https://zhuanlan.zhihu.com/p/397354566\n",
    "# https://torchmetrics.readthedocs.io/en/latest/\n",
    "# https://www.shuzhiduo.com/A/QW5Y6Z3qdm/"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Raw Cell Format",
  "kernelspec": {
   "display_name": "Python 3.7.0 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  },
  "vscode": {
   "interpreter": {
    "hash": "4e894c974a9c6ccfa77845a7dd22544e8f435e3a0dd1c6939db08d30b713c2f0"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
